#include "cc_msg.h"
#include "cc_log.h"
#include "cc_device_msg.h"
#include "cc_mqtt.h"
#include "cc_shell.h"

int g_msg_current_send_node = 0;
uint8_t cc_msg_debug = 0;
cc_msg_node node_arr[CC_MSG_NODE_MAX];

msg_handle_main msg_handle_arr[CC_CONFIG_MSG_MAX_HANDLE_NUM];
msg_handle_main *msg_handle_arr_begin = msg_handle_arr;
msg_handle_main *msg_handle_arr_end = msg_handle_arr;
#define foreach_msg_handle(pos) \
  for (pos = msg_handle_arr_begin; pos < msg_handle_arr_end; pos++)

static void clear_data(void *data, int data_len)
{
  memset(data, 0, data_len);
}

int cc_msg_init()
{
  for (int i = 0; i < CC_MSG_NODE_MAX; i++)
  {
    node_arr[i].que = cc_queue_create(10, sizeof(cc_msg_frame));
    node_arr[i].send_mut = cc_mutex_create();
    node_arr[i].pool =
        cc_mempool_create(10, MEM_BLOCK_REAL_SIZE(sizeof(cc_mnode)));
  }
  return 1;
}
CC_INIT_APP_REGISTER(cc_msg_init);

static int cc_msg_send_mut_lock(int node)
{
  return cc_mutex_lock(node_arr[node].send_mut);
}

static int cc_msg_send_mut_unlock(int node)
{
  return cc_mutex_unlock(node_arr[node].send_mut);
}
// 这个需要锁保护
int cc_msg_send(int node, char *data, int len, char *topic, int topic_len)
{
  if (len == 0)
  {
    cc_log_e("data is null\r\n");
    return CC_EINVAL;
  }
  cc_msg_send_mut_lock(node);
  cc_msg_node send_node = node_arr[node];
  cc_msg_frame frame;
  memset(&frame, 0, sizeof(cc_msg_frame));
  frame.node = node;
  if (data != CC_NULL)
  {
    frame.data = data;
    frame.data_len = len;
  }
  if (topic != CC_NULL)
  {
    frame.topic = topic;
    frame.topic_len = topic_len;
  }

  cc_mnode_t data_node =
      cc_mnode_get(send_node.pool, &frame, sizeof(cc_msg_frame), 100, CC_NULL);
  if (data_node == CC_NULL)
  {
    cc_log_e("get node failed\r\n");
    cc_msg_send_mut_unlock(node);
    return CC_ETIMEDOUT;
  }
  if (cc_msg_debug)
    cc_log("send frame node:%d data:%s data_len:%d\r\n", node, frame.data,
           frame.data_len);
  if (cc_queue_send_time(send_node.que, &data_node, 1000))
  {
    if (cc_msg_debug)
      cc_log("send success\r\n");
  }
  else
  {
    // 回收内存
    cc_log_e("send failed\r\n");
    cc_mnode_put(data_node);
    cc_msg_send_mut_unlock(node);
    return CC_ENOMEM;
  }
  cc_msg_send_mut_unlock(node);
  return CC_EOK;
}

uint8_t msg_handle_cnt = 0;
void cc_msg_handle_register(const char *prefix, cc_msg_handle_fun fun)
{
  if (++msg_handle_cnt > CC_CONFIG_MSG_MAX_HANDLE_NUM)
  {
    cc_log_e("msg handle prefix:%s register failed\r\n", prefix);
    cc_log_e("msg handle arr is full can't register please inc the CC_CONFIG_MSG_MAX_HANDLE_NUM in config.h\r\n");
    return;
  }
  msg_handle_main *entry = msg_handle_arr_end;
  memset(entry->prefix, 0, CC_CONFIG_MSG_HANDLE_PREFIX_MAX_LEN);
  memcpy(entry->prefix, prefix, CC_CONFIG_MSG_HANDLE_PREFIX_MAX_LEN);
  entry->fun = fun;
  msg_handle_arr_end += 1;
  return;
}

int cc_msg_shell_handle(cc_mnode_t data_node)
{
  if (cc_msg_debug)
    cc_log("distr to shell\r\n");
  cc_shell_data_handle(data_node);

  return CC_EOK;
}
CC_MSG_HANDLE_REGISTER(cc_msg_shell_handle, "shell ");

#if (CC_CONFIG_USE_DEVIEMSG == 1)
int cc_msg_device_distr_handle(cc_mnode_t data_node)
{
  if (cc_msg_debug)
    cc_log("distr to d_distr\r\n");
  cc_device_distr_data_handle(data_node);
  return CC_EOK;
}
CC_MSG_HANDLE_REGISTER(cc_msg_device_distr_handle, CC_CONFIG_DEVICE_DISTR_HEAD);
#endif

void cc_msg_distr(cc_mnode_t data_node)
{
  int flag = 0;
  msg_handle_main *ptr = msg_handle_arr_begin;

  cc_msg_frame *frame;
  frame = (cc_msg_frame *)data_node->mem;
  if (strcmp(frame->data, "") == 0)
  {
    cc_log_d("operation is null\r\n");
    return;
  }

  foreach_msg_handle(ptr)
  {
    char head_temp[CC_CONFIG_MSG_HANDLE_PREFIX_MAX_LEN];
    memset(head_temp, 0, CC_CONFIG_MSG_HANDLE_PREFIX_MAX_LEN);
    // head temp就是当前数据对应的头
    memcpy(head_temp, frame->data, strlen(ptr->prefix));
    // cc_log("%d\r\n",strlen(ptr->prefix));
    // cc_log("buf prefix:%s\r\n", ptr->prefix);
    if (strcmp(head_temp, ptr->prefix) == 0)
    {
      if (cc_msg_debug)
      {
        cc_log("success fine the msg handle\r\n");
      }
      ptr->fun(data_node);
      flag = 1;
      break;
    }
  }
  if (!flag)
    cc_log_e("do not have the msg handle(data:%s)\r\n", frame->data);
  return;
}

static char result[512] = "";
static void show_msg_handle(char *param, int node)
{
  msg_handle_main *ptr = msg_handle_arr_begin;

  char temp[40] = "";
  strcpy(result, "\r\n--------support prefix-------\r\n");
  foreach_msg_handle(ptr)
  {
    memset(temp, 0, sizeof(temp));
    if (ptr->fun && (strcmp(ptr->prefix, "") != 0))
    {
      sprintf(temp, "%s\r\n", ptr->prefix);
      strcat(result, temp);
    }
  }
  strcat(result, "-------------------------------\r\n");
  cc_msg_resp(node, result, 0, CC_NULL);
  return;
}
CC_COMMAND_SHELL(show_msg_handle, "show all prefix of shell support")

void cc_msg_handle()
{
  cc_mnode_t data_node;
  cc_msg_frame *frame;
  for (size_t i = 0; i < CC_MSG_NODE_MAX; i++)
  {

    cc_msg_node recv_node = node_arr[i];
    if (recv_node.que == CC_NULL || recv_node.pool == CC_NULL || recv_node.send_mut == CC_NULL)
    {
      break;
    }
    if (cc_queue_recv_time(recv_node.que, &data_node, 100))
    {
      frame = (cc_msg_frame *)data_node->mem;
      frame->node = i;
      if (frame->data != CC_NULL)
      {
        if (cc_msg_debug)
        {
          cc_log("recv frame node:%d data:%s data_len:%d\r\n", frame->node,
                 frame->data, frame->data_len);
        }
        cc_msg_distr(data_node);
        // if (frame->data != CC_NULL)
        // {
        //   cc_free(frame->data);
        //   frame->data = CC_NULL;
        // }

        cc_mnode_put(data_node);
      }
    }
    /* code */
  }
}

void cc_msg_resp(int node, const char *data, int len, const char *topic)
{
  if (node == CC_MSG_SERIAL_NODE)
  {
    cc_log("%s", data);
  }
#if (CC_CONFIG_USE_MQTT && CC_CONFIG_USE_WIFI)
  if (node == CC_MSG_MQTT_NODE)
  {
    cc_msg_mqtt_publish("cc_msg_resp", data);
  }
#endif
}

void cc_msg_entry(void *data)
{
  while (1)
  {

    cc_msg_handle();
  }
}
CC_THREAD_REGISTER_ALIAS(cc_msg_entry, 31, 3072, "msg_entry");

#if (LOG_D == 1)
void msg_debug(char *param, int node)
{
  cc_log_d("recv from node(%d):%s\r\n", node, param);
  if (cc_is_all_digits(param))
  {
    cc_msg_debug = atoi(param);
    if (cc_msg_debug < 0 || cc_msg_debug > 1)
    {
      cc_log_e("need 1 or 0\r\n");
      return;
    }
  }
  else
  {
    cc_log_e("need all digit param\r\n");
    return;
  }
  if (cc_msg_debug)
  {
    cc_msg_resp(node, "msg debug open\r\n", 0, 0);
  }
  else
  {
    cc_msg_resp(node, "msg debug close\r\n", 0, 0);
  }
}
CC_COMMAND_SHELL(msg_debug, "enable(1) disable(0) msg debug");
#endif
